package com.bigkoo.pickerview.lib;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import com.bigkoo.pickerview.R;
import com.bigkoo.pickerview.adapter.WheelAdapter;
import com.bigkoo.pickerview.listener.OnItemSelectedListener;
import com.bigkoo.pickerview.model.IPickerViewData;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * 3d滚轮控件
 */
public class WheelView extends View {

  public enum ACTION {
    // 点击，滑翔(滑到尽头)，拖拽事件
    CLICK, FLING, DAGGLE
  }

  Context context;

  Handler handler;
  private GestureDetector gestureDetector;
  OnItemSelectedListener onItemSelectedListener;

  // Timer mTimer;
  ScheduledExecutorService mExecutor = Executors.newSingleThreadScheduledExecutor();
  private ScheduledFuture<?> mFuture;

  Paint paintOuterText;
  Paint paintCenterText;
  Paint paintIndicator;

  WheelAdapter adapter;

  private String label;//附加单位
  int textSize;//选项的文字大小
  // fixme 自定义文字大小，为true则用于使setTextSize函数无效，只能通过xml修改
  boolean customTextSize = true;
  int maxTextWidth;
  int maxTextHeight;
  float itemHeight;//每行高度

  int textColorOut;
  int textColorCenter;
  int dividerColor;

  // 条目间距倍数
  static final float lineSpacingMultiplier = 3.F;
  boolean isLoop;

  // 第一条线Y坐标值
  float firstLineY;
  //第二条线Y坐标
  float secondLineY;
  //中间Y坐标
  float centerY;

  //滚动总高度y值
  int totalScrollY;
  //初始化默认选中第几个
  int initPosition;
  //选中的Item是第几个
  private int selectedItem;
  int preCurrentIndex;
  //滚动偏移值,用于记录滚动了多少个item
  int change;

  // 显示几个条目,必须为奇数
  int itemsVisible = 5;

  int measuredHeight;
  int measuredWidth;

  // 半圆周长
  //int halfCircumference;
  // 半径
  int radius;

  private int mOffset = 0;
  private float previousY = 0;
  long startTime = 0;

  // 修改这个值可以改变滑行速度
  private static final int VELOCITYFLING = 5;
  int widthMeasureSpec;

  private int mGravity = Gravity.CENTER;
  private int drawCenterContentStart = 0;//中间选中文字开始绘制位置
  private int drawOutContentStart = 0;//非中间文字开始绘制位置
  //private static final float SCALECONTENT = 0.8F;//非中间文字则用此控制高度，压扁形成3d错觉
  private static final float CENTERCONTENTOFFSET = 6;//中间文字文字居中需要此偏移值

  public WheelView(Context context) {
    this(context, null);
  }

  public WheelView(Context context, AttributeSet attrs) {
    super(context, attrs);
    textColorOut = getResources().getColor(R.color.pickerview_wheelview_textcolor_out);
    textColorCenter = getResources().getColor(R.color.pickerview_wheelview_textcolor_center);
    dividerColor = getResources().getColor(R.color.pickerview_wheelview_textcolor_divider);

    //配合customTextSize使用，customTextSize为true才会发挥效果
    if (attrs != null) {
      TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.pickerview, 0, 0);
      mGravity = a.getInt(R.styleable.pickerview_pickerview_gravity, Gravity.CENTER);
      textColorOut = a.getColor(R.styleable.pickerview_pickerview_textColorOut, textColorOut);
      textColorCenter =
          a.getColor(R.styleable.pickerview_pickerview_textColorCenter, textColorCenter);
      dividerColor = a.getColor(R.styleable.pickerview_pickerview_dividerColor, dividerColor);
      textSize = a.getDimensionPixelOffset(R.styleable.pickerview_pickerview_textSize,
          getResources().getDimensionPixelSize(R.dimen.pickerview_item_textsize));
      customTextSize = a.getBoolean(R.styleable.pickerview_custom_textSize, true);
      a.recycle();
    }
    initLoopView(context);
  }

  private void initLoopView(Context context) {
    this.context = context;
    handler = new MessageHandler(this);
    gestureDetector = new GestureDetector(context, new LoopViewGestureListener(this));
    gestureDetector.setIsLongpressEnabled(false);

    isLoop = true;

    totalScrollY = 0;
    initPosition = -1;

    initPaints();
  }

  private void initPaints() {
    paintOuterText = new Paint();
    paintOuterText.setColor(textColorOut);
    paintOuterText.setAntiAlias(true);
    paintOuterText.setTypeface(Typeface.MONOSPACE);
    paintOuterText.setTextSize(textSize);

    paintCenterText = new Paint();
    paintCenterText.setColor(textColorCenter);
    paintCenterText.setAntiAlias(true);
    //paintCenterText.setTextScaleX(1.1F);
    paintCenterText.setTypeface(Typeface.MONOSPACE);
    paintCenterText.setTextSize(textSize);

    paintIndicator = new Paint();
    paintIndicator.setColor(dividerColor);
    paintIndicator.setAntiAlias(true);

    if (android.os.Build.VERSION.SDK_INT >= 11) {
      setLayerType(LAYER_TYPE_SOFTWARE, null);
    }
  }

  private void remeasure() {
    if (adapter == null) {
      return;
    }

    measureTextWidthHeight();

    //最大Text的高度乘间距倍数得到 可见文字实际的总高度，半圆的周长
    //halfCircumference = (int) (itemHeight * (itemsVisible - 1)) ;
    ////整个圆的周长除以PI得到直径，这个直径用作控件的总高度
    //measuredHeight = (int) ((halfCircumference * 2) / Math.PI);
    ////求出半径
    //radius = (int) (halfCircumference / Math.PI);
    measuredHeight = (int) (itemHeight * itemsVisible);
    //控件宽度，这里支持weight
    measuredWidth = MeasureSpec.getSize(widthMeasureSpec);
    //计算两条横线和控件中间点的Y位置
    firstLineY = (measuredHeight - itemHeight) / 2.0F;
    secondLineY = (measuredHeight + itemHeight) / 2.0F;
    centerY = (measuredHeight + maxTextHeight) / 2.0F - CENTERCONTENTOFFSET;
    //初始化显示的item的position，根据是否loop
    if (initPosition == -1) {
      if (isLoop) {
        initPosition = (adapter.getItemsCount() + 1) / 2;
      } else {
        initPosition = 0;
      }
    }

    preCurrentIndex = initPosition;
  }

  /**
   * 计算最大len的Text的宽高度
   */
  private void measureTextWidthHeight() {
    Rect rect = new Rect();
    for (int i = 0; i < adapter.getItemsCount(); i++) {
      String s1 = getContentText(adapter.getItem(i));
      paintCenterText.getTextBounds(s1, 0, s1.length(), rect);
      int textWidth = rect.width();
      if (textWidth > maxTextWidth) {
        maxTextWidth = textWidth;
      }
      paintCenterText.getTextBounds("\u661F\u671F", 0, 2, rect); // 星期
      int textHeight = rect.height();
      if (textHeight > maxTextHeight) {
        maxTextHeight = textHeight;
      }
    }
    //itemHeight = lineSpacingMultiplier * maxTextHeight;
    itemHeight = context.getResources().getDimension(R.dimen.pickerview_item_height);
  }

  void smoothScroll(ACTION action) {
    cancelFuture();
    if (action == ACTION.FLING || action == ACTION.DAGGLE) {
      mOffset = (int) ((totalScrollY % itemHeight + itemHeight) % itemHeight);
      if ((float) mOffset > itemHeight / 2.0F) {
        mOffset = (int) (itemHeight - (float) mOffset);
      } else {
        mOffset = -mOffset;
      }
    }

    //
    if (mOffset > 0) {
      mOffset = mOffset + 1;
    } else if (mOffset < 0) {
      mOffset = mOffset - 1;
    }
    //停止的时候，位置有偏移，不是全部都能正确停止到中间位置的，这里把文字位置挪回中间去
    mFuture = mExecutor.scheduleWithFixedDelay(new SmoothScrollTimerTask(this, mOffset), 0, 10,
        TimeUnit.MILLISECONDS);
  }

  protected final void scrollBy(float velocityY) {
    cancelFuture();

    mFuture =
        mExecutor.scheduleWithFixedDelay(new InertiaTimerTask(this, velocityY), 0, VELOCITYFLING,
            TimeUnit.MILLISECONDS);
  }

  public void cancelFuture() {
    if (mFuture != null && !mFuture.isCancelled()) {
      mFuture.cancel(true);
      mFuture = null;
    }
  }

  /**
   * 设置是否循环滚动
   *
   * @param cyclic 是否循环
   */
  public final void setCyclic(boolean cyclic) {
    isLoop = cyclic;
  }

  /**
   * @param size dp
   */
  public final void setTextSize(float size) {
    if (size > 0.0F && !customTextSize) {
      textSize = (int) (context.getResources().getDisplayMetrics().density * size);
      paintOuterText.setTextSize(textSize);
      paintCenterText.setTextSize(textSize);
    }
  }

  public final void setCurrentItem(int currentItem) {
    this.initPosition = currentItem;
    totalScrollY = 0;//回归顶部，不然重设setCurrentItem的话位置会偏移的，就会显示出不对位置的数据
    invalidate();
  }

  public final void setOnItemSelectedListener(OnItemSelectedListener OnItemSelectedListener) {
    this.onItemSelectedListener = OnItemSelectedListener;
  }

  public final void setAdapter(WheelAdapter adapter) {
    this.adapter = adapter;
    remeasure();
    invalidate();
  }

  public final WheelAdapter getAdapter() {
    return adapter;
  }

  public final int getCurrentItem() {
    return selectedItem;
  }

  protected final void onItemSelected() {
    if (onItemSelectedListener != null) {
      postDelayed(new OnItemSelectedRunnable(this), 200L);
    }
  }

  @Override protected void onDraw(Canvas canvas) {
    if (adapter == null) {
      return;
    }
    //可见的item数组
    Object visibles[] = new Object[itemsVisible];
    //滚动的Y值高度除去每行Item的高度，得到滚动了多少个item，即change数
    change = (int) (totalScrollY / itemHeight);
    //Log.i("fcy", "----------------- totalScrollY=" + totalScrollY + ",itemHeight=" + itemHeight);
    try {
      //滚动中实际的预选中的item(即经过了中间位置的item) ＝ 滑动前的位置 ＋ 滑动相对位置
      preCurrentIndex = initPosition + change % adapter.getItemsCount();
    } catch (ArithmeticException e) {
      System.out.println("出错了！adapter.getItemsCount() == 0，联动数据不匹配");
    }
    if (!isLoop) {//不循环的情况
      if (preCurrentIndex < 0) {
        preCurrentIndex = 0;
      }
      if (preCurrentIndex > adapter.getItemsCount() - 1) {
        preCurrentIndex = adapter.getItemsCount() - 1;
      }
    } else {//循环
      if (preCurrentIndex
          < 0) {//举个例子：如果总数是5，preCurrentIndex ＝ －1，那么preCurrentIndex按循环来说，其实是0的上面，也就是4的位置
        preCurrentIndex = adapter.getItemsCount() + preCurrentIndex;
      }
      if (preCurrentIndex > adapter.getItemsCount() - 1) {//同理上面,自己脑补一下
        preCurrentIndex = preCurrentIndex - adapter.getItemsCount();
      }
    }

    //跟滚动流畅度有关，总滑动距离与每个item高度取余，即并不是一格格的滚动，每个item不一定滚到对应Rect里的，这个item对应格子的偏移值
    int itemHeightOffset = (int) (totalScrollY % itemHeight);
    // 设置数组中每个元素的值
    int counter = 0;
    while (counter < itemsVisible) {
      int index = preCurrentIndex - (itemsVisible / 2
          - counter);//索引值，即当前在控件中间的item看作数据源的中间，计算出相对源数据源的index值

      //判断是否循环，如果是循环数据源也使用相对循环的position获取对应的item值，如果不是循环则超出数据源范围使用""空白字符串填充，在界面上形成空白无数据的item项
      if (isLoop) {
        index = getLoopMappingIndex(index);
        visibles[counter] = adapter.getItem(index);
      } else if (index < 0) {
        visibles[counter] = "";
      } else if (index > adapter.getItemsCount() - 1) {
        visibles[counter] = "";
      } else {
        visibles[counter] = adapter.getItem(index);
      }

      counter++;
    }

    //中间两条横线
    canvas.drawLine(0.0F, firstLineY, measuredWidth, firstLineY, paintIndicator);
    canvas.drawLine(0.0F, secondLineY, measuredWidth, secondLineY, paintIndicator);
    //单位的Label
    if (label != null) {
      // rebuild by fcy 将label绘制到内容右边
      int offset = (measuredWidth + maxTextWidth) / 2 + 6;
      canvas.drawText(label, offset, centerY, paintCenterText);

      //靠右并留出空隙
      //int drawRightContentStart = measuredWidth - getTextWidth(paintCenterText, label);
      //            canvas.drawText(label, drawRightContentStart - CENTERCONTENTOFFSET, centerY, paintCenterText);
    }
    counter = 0;
    while (counter < itemsVisible) {
      canvas.save();
      String contentText = getContentText(visibles[counter]);

      //计算开始绘制的位置
      measuredCenterContentStart(contentText);
      measuredOutContentStart(contentText);
      float translateY = itemHeight * counter + itemHeight / 2 - maxTextHeight / 2 -
          itemHeightOffset;
      //根据Math.sin(radian)来更改canvas坐标系原点，然后缩放画布，使得文字高度进行缩放，形成弧形3d视觉差
      canvas.translate(0.0F, translateY);
      if (translateY <= firstLineY && maxTextHeight + translateY >= firstLineY) {
        // 条目经过第一条线
        canvas.save();
        canvas.clipRect(0, 0, measuredWidth, firstLineY - translateY);
        canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
        canvas.restore();
        canvas.save();
        canvas.clipRect(0, firstLineY - translateY, measuredWidth, (int) (itemHeight));
        canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET,
            paintCenterText);
        canvas.restore();
      } else if (translateY <= secondLineY && maxTextHeight + translateY >= secondLineY) {
        // 条目经过第二条线
        canvas.save();
        canvas.clipRect(0, 0, measuredWidth, secondLineY - translateY);
        canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET,
            paintCenterText);
        canvas.restore();
        canvas.save();
        canvas.clipRect(0, secondLineY - translateY, measuredWidth, (int) (itemHeight));
        canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
        canvas.restore();
      } else if (translateY >= firstLineY && maxTextHeight + translateY <= secondLineY) {
        // 中间条目
        canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
        canvas.drawText(contentText, drawCenterContentStart, maxTextHeight - CENTERCONTENTOFFSET,
            paintCenterText);
        int preSelectedItem = adapter.indexOf(visibles[counter]);
        if (preSelectedItem != -1) {
          selectedItem = preSelectedItem;
        }
      } else {
        // 其他条目
        canvas.save();
        canvas.clipRect(0, 0, measuredWidth, (int) (itemHeight));
        canvas.drawText(contentText, drawOutContentStart, maxTextHeight, paintOuterText);
        canvas.restore();
      }
      canvas.restore();
      counter++;
    }
  }

  //递归计算出对应的index
  private int getLoopMappingIndex(int index) {
    if (index < 0) {
      index = index + adapter.getItemsCount();
      index = getLoopMappingIndex(index);
    } else if (index > adapter.getItemsCount() - 1) {
      index = index - adapter.getItemsCount();
      index = getLoopMappingIndex(index);
    }
    return index;
  }

  /**
   * 根据传进来的对象获取getPickerViewText()方法，来获取需要显示的值
   *
   * @param item 数据源的item
   * @return 对应显示的字符串
   */
  private String getContentText(Object item) {
    if (item == null) {
      return "";
    } else if (item instanceof IPickerViewData) {
      return ((IPickerViewData) item).getPickerViewText();
    }
    return item.toString();
  }

  private void measuredCenterContentStart(String content) {
    Rect rect = new Rect();
    paintCenterText.getTextBounds(content, 0, content.length(), rect);
    switch (mGravity) {
      case Gravity.CENTER:
        drawCenterContentStart = (int) ((measuredWidth - rect.width()) * 0.5);
        break;
      case Gravity.LEFT:
        drawCenterContentStart = 0;
        break;
      case Gravity.RIGHT:
        drawCenterContentStart = measuredWidth - rect.width();
        break;
    }
  }

  private void measuredOutContentStart(String content) {
    Rect rect = new Rect();
    paintOuterText.getTextBounds(content, 0, content.length(), rect);
    switch (mGravity) {
      case Gravity.CENTER:
        drawOutContentStart = (int) ((measuredWidth - rect.width()) * 0.5);
        break;
      case Gravity.LEFT:
        drawOutContentStart = 0;
        break;
      case Gravity.RIGHT:
        drawOutContentStart = measuredWidth - rect.width();
        break;
    }
  }

  @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    this.widthMeasureSpec = widthMeasureSpec;
    remeasure();
    setMeasuredDimension(measuredWidth, measuredHeight);
  }

  @Override public boolean onTouchEvent(MotionEvent event) {
    boolean eventConsumed = gestureDetector.onTouchEvent(event);
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        startTime = System.currentTimeMillis();
        cancelFuture();
        previousY = event.getRawY();
        break;

      case MotionEvent.ACTION_MOVE:
        float dy = previousY - event.getRawY();
        previousY = event.getRawY();
        totalScrollY = (int) (totalScrollY + dy);

        // 边界处理。
        if (!isLoop) {
          float top = -initPosition * itemHeight;
          float bottom = (adapter.getItemsCount() - 1 - initPosition) * itemHeight;
          if (totalScrollY - itemHeight * 0.3 < top) {
            top = totalScrollY - dy;
          } else if (totalScrollY + itemHeight * 0.3 > bottom) {
            bottom = totalScrollY - dy;
          }

          if (totalScrollY < top) {
            totalScrollY = (int) top;
          } else if (totalScrollY > bottom) {
            totalScrollY = (int) bottom;
          }
        }
        break;

      case MotionEvent.ACTION_UP:
      default:
        if (!eventConsumed) {
          int count = (int) Math.ceil(event.getY() / itemHeight);

          float extraOffset = (totalScrollY % itemHeight + itemHeight) % itemHeight;

          mOffset = (int) ((count - itemsVisible / 2 - 1) * itemHeight - extraOffset);
          if ((System.currentTimeMillis() - startTime) > 120) {
            // 处理拖拽事件
            smoothScroll(ACTION.DAGGLE);
          } else {
            // 处理条目点击事件
            smoothScroll(ACTION.CLICK);
          }
        }
        break;
    }
    invalidate();

    return true;
  }

  /**
   * 获取Item个数
   *
   * @return item个数
   */
  public int getItemsCount() {
    return adapter != null ? adapter.getItemsCount() : 0;
  }

  /**
   * 附加在右边的单位字符串
   *
   * @param label 单位
   */
  public void setLabel(String label) {
    this.label = label;
  }

  public void setGravity(int gravity) {
    this.mGravity = gravity;
  }

  public int getTextWidth(Paint paint, String str) {
    int iRet = 0;
    if (str != null && str.length() > 0) {
      int len = str.length();
      float[] widths = new float[len];
      paint.getTextWidths(str, widths);
      for (int j = 0; j < len; j++) {
        iRet += (int) Math.ceil(widths[j]);
      }
    }
    return iRet;
  }
}